<?php

declare (strict_types=1);

namespace app\common\service;


use app\common\model\UserDigitalHuman;
use app\common\model\VoiceLab;
use GuzzleHttp\Client;
use think\facade\Config;

class ElevenLabService extends CommonBaseService
{
    protected array $config;
    protected array $options;
    protected Client $client;

    /**
     * 构造方法
     * ElevenLabService constructor.
     */
    public function __construct()
    {
        $this->config = Config::get('tripartite_setting.api_config');
        $this->options = [
            'headers'=>[
                'xi-api-key'=>$this->config['elevenlabs_apikey'],
                'Accept' => '*/*',
                'Connection' => 'keep-alive',
            ],
            'verify' => false
        ];
        $this->client = new Client(['base_uri' => $this->config['elevenlabs_host']]);
    }


    /**
     * @param UserDigitalHuman $udh
     * @return bool
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function addVoice2(UserDigitalHuman $udh): bool{

        $name = date('Y-m-d H:i:s');
        $this->options['multipart'] = [
//            [
//                'name' => 'description',
//                'contents' => ''
//            ],
            [
                'name' => 'name',
                'contents' => $name
            ]
        ];

        $voice = VoiceLab::where(['user_id'=>$udh->user_id,'user_dh_id'=>$udh->id])->find();
        foreach (json_decode($voice->url,true) as $url){
            $this->options['multipart'][] = [
                'name' => 'files',
                'contents' => fopen(app()->getRootPath()."public".$url, 'r'),
                'filename' => pathinfo($url)['basename'],
                'headers'  => [
                    'Content-Type' => '<Content-type header>'
                ]
            ];
        }

        $content = $this->client
            ->post('/v1/voices/add', $this->options)
            ->getBody()
            ->getContents();
        $data = json_decode($content,true);
        if (!isset($data['voice_id'])){
            throw new \Exception('添加语音失败，请重新再试或联系技术人员。');
        }
        //创建成功后保存至数据库
        $voice->voice_id    = $data['voice_id'];
        $voice->name        = $name;
        $voice->description = '';

        if(!$voice->save()){
            return false;
        }

        $udh->create_status = UserDigitalHuman::CREATE_STATUS[2];
        return $udh->save();
    }

    /**
     * @param array $param
     * @param array $files
     * @return array
     * @throws \Exception
     */
    public function addVoice(array $param,array $files): array{

        $this->options['multipart'] = [
            [
                'name' => 'description',
                'contents' => $param['description']
            ],
            [
                'name' => 'name',
                'contents' => $param['name']
            ]
        ];
        foreach ($files as $file){
            $this->options['multipart'][] = [
                'name' => 'files',
                'contents' => fopen($file->getPathname(), 'r'),
                'filename' => $file->getOriginalName(),
                'headers'  => [
                    'Content-Type' => '<Content-type header>'
                ]
            ];
        }
        $content = $this->client
            ->post('/v1/voices/add', $this->options)
            ->getBody()
            ->getContents();
        $data = json_decode($content,true);

        if (!isset($data['voice_id'])){
            throw new \Exception('添加语音失败，请重新再试或联系技术人员。');
        }
        //创建成功后保存至数据库
        $voice              = new VoiceLab();
        $voice->user_id     = $param['user_id'];
        $voice->voice_id    = $data['voice_id'];
        $voice->name        = $param['name'];
        $voice->description = $param['description'];
        $voice->save();

        return $data;
    }

    /**
     * @param array $param
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getVoices(array $param){
        $list = VoiceLab::where('user_id',$param['user_id'])
                ->field("id,voice_id,name,description,preview_url,samples,create_time")
                ->select()
                ->toArray();
        return ['list'=>$list];
    }

    /**
     * 旧的方法，直接通过接口调用官方的所有数据
     * @param array $param
     * @return array
     */
    public function voices(array $param): array {
        $response = $this->client->get('/v1/voices', $this->options);

        $data = [];
        $tmp_data = json_decode($response->getBody()->getContents(),true);
        $insert_data = [];
        foreach ($tmp_data['voices'] as $voice){
            if ($voice['category'] == 'cloned'){
                $data[] = $voice;
            }
            $insert_data[] = [
                'user_id'       =>$param['user_id'],
                'voice_id'      =>$voice['voice_id'],
                'name'          =>$voice['name'],
                'description'   =>$voice['description'],
                'preview_url'   =>$voice['preview_url'],
                'samples'       =>json_encode($voice['samples'])
            ];
        }
        $m = new VoiceLab();
        $m->saveAll($insert_data);
        return $data;
    }

    /**
     * @param array $param
     * @return int
     */
    public function textToSpeech(array $param): int {

        $this->options['json']  = [
            'model_id'=>'eleven_multilingual_v2',
            'text'=>$param['text'],
            'voice_settings'=>[
                'similarity_boost'=>0.75,
                'stability'=>0.5,
                'style'=>0,
                'use_speaker_boost'=>true
            ]
        ];
        $response  = $this->client->post('/v1/text-to-speech/'.$param['voice_id'], $this->options);

        return $response->getStatusCode();
    }

    /**
     * @param array $param
     * @param int $limit
     * @return String
     */
    public function history(array $param,int $limit): String{

        $query = ['page_size'=>$limit];

        if (!empty($param['start_after_history_item_id'])){
            $query['start_after_history_item_id'] = $param['start_after_history_item_id'];
        }
        $this->options['query']= $query;
        return $this->client
            ->get('/v1/history', $this->options)
            ->getBody()->getContents();
    }
}